package com.distributed.kv.service;

import com.distributed.kv.entity.KvPair;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheStats;

import javax.annotation.PostConstruct;

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * 多级缓存服务
 * 
 * 架构设计:
 * L1: 本地Guava Cache - 最快访问,容量有限
 * L2: Redis应用级缓存 - 跨实例共享,容量较大
 * L3: Cassandra后端存储 - 持久化,容量无限
 * 
 * 缓存策略:
 * - 读取: L1 -> L2 -> L3 (Cache-Aside模式)
 * - 写入: 先写L3,成功后更新L1和L2
 * - 失效: LRU + TTL自动过期
 */
@Service
public class CacheService {
    
    private static final Logger logger = LoggerFactory.getLogger(CacheService.class);
    
    private final RedissonClient redissonClient;
    
    // Guava Cache配置
    @Value("${cache.local.guava.maximum-size:100000}")
    private long maxSize;
    
    @Value("${cache.local.guava.expire-after-write:30m}")
    private String expireAfterWrite;
    
    @Value("${cache.local.guava.expire-after-access:10m}")
    private String expireAfterAccess;
    
    // Redis Cache配置
    @Value("${cache.local.redis.default-ttl:1800}")
    private int defaultTtl;
    
    @Value("${cache.local.redis.hot-key-ttl:3600}")
    private int hotKeyTtl;
    
    // L1缓存: 本地Guava Cache
    private Cache<String, KvPair> localCache;
    
    public CacheService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }
    
    @PostConstruct
    public void init() {
        // 初始化Guava本地缓存
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder()
                .maximumSize(maxSize)
                .recordStats(); // 启用统计
        
        // 解析并设置TTL
        builder.expireAfterWrite(parseDuration(expireAfterWrite).toMillis(), TimeUnit.MILLISECONDS);
        builder.expireAfterAccess(parseDuration(expireAfterAccess).toMillis(), TimeUnit.MILLISECONDS);
        
        localCache = builder.build();
        
        logger.info("缓存服务初始化完成 - 本地缓存大小: {}, 写入过期: {}, 访问过期: {}", 
                   maxSize, expireAfterWrite, expireAfterAccess);
    }
    
    /**
     * 获取KV对 - 多级缓存查找
     */
    public KvPair get(String key) {
        // L1: 本地缓存
        KvPair kvPair = localCache.getIfPresent(key);
        if (kvPair != null) {
            logger.debug("L1缓存命中: {}", key);
            kvPair.incrementAccessCount();
            return kvPair;
        }
        
        // L2: Redis缓存
        kvPair = getFromRedis(key);
        if (kvPair != null) {
            logger.debug("L2缓存命中: {}", key);
            localCache.put(key, kvPair); // 回填L1
            kvPair.incrementAccessCount();
            return kvPair;
        }
        
        logger.debug("缓存未命中: {}", key);
        return null;
    }
    
    /**
     * 存储KV对到缓存
     */
    public void put(String key, KvPair kvPair) {
        // 同时更新L1和L2缓存
        localCache.put(key, kvPair);
        putToRedis(key, kvPair, isHotKey(kvPair) ? hotKeyTtl : defaultTtl);
        
        logger.debug("缓存已更新: {} -> ID:{}", key, kvPair.getId());
    }
    
    /**
     * 从缓存中移除
     */
    public void evict(String key) {
        localCache.invalidate(key);
        removeFromRedis(key);
        
        logger.debug("缓存已清理: {}", key);
    }
    
    /**
     * 批量移除
     */
    public void evictAll(Iterable<String> keys) {
        localCache.invalidateAll(keys);
        
        for (String key : keys) {
            removeFromRedis(key);
        }
    }
    
    /**
     * 清空所有缓存
     */
    public void clear() {
        localCache.invalidateAll();
        // Redis清理通过TTL自然过期,避免误删其他业务数据
        
        logger.info("本地缓存已清空");
    }
    
    /**
     * 预热缓存 - 将热点数据加载到L1缓存
     */
    public void warmUp(String key, KvPair kvPair) {
        if (kvPair != null) {
            localCache.put(key, kvPair);
            putToRedis(key, kvPair, hotKeyTtl);
            
            logger.debug("缓存预热: {}", key);
        }
    }
    
    /**
     * 获取缓存统计信息
     */
    public CacheStatistics getStatistics() {
        CacheStats stats = localCache.stats();
        
        return new CacheStatistics(
            localCache.size(),
            stats.hitCount(),
            stats.missCount(),
            stats.hitRate(),
            stats.evictionCount(),
            stats.loadCount()
        );
    }
    
    // === 私有方法 ===
    
    private KvPair getFromRedis(String key) {
        try {
            RBucket<KvPair> bucket = redissonClient.getBucket("kv:" + key);
            return bucket.get();
        } catch (Exception e) {
            logger.warn("Redis读取失败: {} - {}", key, e.getMessage());
            return null;
        }
    }
    
    private void putToRedis(String key, KvPair kvPair, int ttlSeconds) {
        try {
            RBucket<KvPair> bucket = redissonClient.getBucket("kv:" + key);
            bucket.set(kvPair, ttlSeconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.warn("Redis写入失败: {} - {}", key, e.getMessage());
        }
    }
    
    private void removeFromRedis(String key) {
        try {
            RBucket<KvPair> bucket = redissonClient.getBucket("kv:" + key);
            bucket.delete();
        } catch (Exception e) {
            logger.warn("Redis删除失败: {} - {}", key, e.getMessage());
        }
    }
    
    private boolean isHotKey(KvPair kvPair) {
        // 简单的热点判断逻辑:访问次数 > 10
        return kvPair.getAccessCount() != null && kvPair.getAccessCount() > 10;
    }
    
    private Duration parseDuration(String duration) {
        try {
            if (duration.endsWith("m")) {
                return Duration.ofMinutes(Long.parseLong(duration.substring(0, duration.length() - 1)));
            } else if (duration.endsWith("h")) {
                return Duration.ofHours(Long.parseLong(duration.substring(0, duration.length() - 1)));
            } else if (duration.endsWith("s")) {
                return Duration.ofSeconds(Long.parseLong(duration.substring(0, duration.length() - 1)));
            } else {
                return Duration.ofSeconds(Long.parseLong(duration));
            }
        } catch (Exception e) {
            logger.warn("无法解析时间配置: {}, 使用默认值30分钟", duration);
            return Duration.ofMinutes(30);
        }
    }
    
    /**
     * 缓存统计信息
     */
    public static class CacheStatistics {
        private final long size;
        private final long hitCount;
        private final long missCount;
        private final double hitRate;
        private final long evictionCount;
        private final long loadCount;
        
        public CacheStatistics(long size, long hitCount, long missCount, 
                             double hitRate, long evictionCount, long loadCount) {
            this.size = size;
            this.hitCount = hitCount;
            this.missCount = missCount;
            this.hitRate = hitRate;
            this.evictionCount = evictionCount;
            this.loadCount = loadCount;
        }
        
        // Getters
        public long getSize() { return size; }
        public long getHitCount() { return hitCount; }
        public long getMissCount() { return missCount; }
        public double getHitRate() { return hitRate; }
        public long getEvictionCount() { return evictionCount; }
        public long getLoadCount() { return loadCount; }
        public long getTotalRequestCount() { return hitCount + missCount; }
    }
}